﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Threading;
using System.Reflection;
using Sharpsoft.Controls;
using System.Windows.Markup;
using System.IO;

namespace SharpSoft.Controls
{
    public class DrawingBrush : TileBrush
    {

        #region Drawing

        /// <summary> 
        /// Gets or sets the Drawing possible Value of the Shape object.
        /// </summary> 
        public Shape Drawing
        {
            get { return (Shape)GetValue(DrawingProperty); }
            set { SetValue(DrawingProperty, value); }
        }

        /// <summary> 
        /// Identifies the Drawing dependency property.
        /// </summary> 
        public static readonly DependencyProperty DrawingProperty =
                    DependencyProperty.Register(
                          "Drawing",
                          typeof(Shape),
                          typeof(DrawingBrush),
                          new PropertyMetadata(OnDrawingPropertyChanged));

        /// <summary>
        /// DrawingProperty property changed handler. 
        /// </summary>
        /// <param name="d">DrawingBrush that changed its Drawing.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param> 
        private static void OnDrawingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DrawingBrush _DrawingBrush = d as DrawingBrush;
            if (_DrawingBrush != null)
            {
                _DrawingBrush.ApplyBrush();
            }
        }


        #endregion Drawing

        #region Viewport

        /// <summary> 
        /// Gets or sets the Viewport possible Value of the Rect object.
        /// </summary> 
        public Rect Viewport
        {
            get { return (Rect)GetValue(ViewportProperty); }
            set { SetValue(ViewportProperty, value); }
        }

        /// <summary> 
        /// Identifies the Viewport dependency property.
        /// </summary> 
        public static readonly DependencyProperty ViewportProperty =
                    DependencyProperty.Register(
                          "Viewport",
                          typeof(Rect),
                          typeof(DrawingBrush),
                          new PropertyMetadata(OnViewportPropertyChanged));

        /// <summary>
        /// ViewportProperty property changed handler. 
        /// </summary>
        /// <param name="d">DrawingBrush that changed its Viewport.</param>
        /// <param name="e">DependencyPropertyChangedEventArgs.</param> 
        private static void OnViewportPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DrawingBrush _DrawingBrush = d as DrawingBrush;
            if (_DrawingBrush != null)
            {
                _DrawingBrush.ApplyBrush();
            }
        }
        #endregion Viewport

        #region Pattern

        /// <summary> 
        /// Gets or sets the Pattern possible Value of the UIElement object.
        /// </summary> 
        protected WrapPanel Pattern
        {
            get { return (WrapPanel)GetValue(PatternProperty); }
            set { SetValue(PatternProperty, value); }
        }

        /// <summary> 
        /// Identifies the Pattern dependency property.
        /// </summary> 
        protected static readonly DependencyProperty PatternProperty =
                    DependencyProperty.Register(
                          "Pattern",
                          typeof(WrapPanel),
                          typeof(DrawingBrush),
                          null);
        #endregion Pattern


        #region TileMode

        /// <summary> 
        /// Gets or sets the TileMode possible Value of the TileMode object.
        /// </summary> 
        public TileMode TileMode
        {
            get { return (TileMode)GetValue(TileModeProperty); }
            set { SetValue(TileModeProperty, value); }
        }

        /// <summary> 
        /// Identifies the TileMode dependency property.
        /// </summary> 
        public static readonly DependencyProperty TileModeProperty =
                    DependencyProperty.Register(
                          "TileMode",
                          typeof(TileMode),
                          typeof(DrawingBrush),
                          null);
        
        #endregion TileMode


        protected void ApplyBrush()
        {
            if (Application.Current.RootVisual != null)
            {
                ApplyBrushImpl(null, null);
            }
            else
            {
                DispatcherTimer dt = new DispatcherTimer();
                dt.Interval = TimeSpan.FromMilliseconds(100);
                dt.Tick += ApplyBrushImpl;
                dt.Start();
            }
        }


        void ApplyBrushImpl(object sender, EventArgs e)
        {
            if (Application.Current.RootVisual != null)
            {
                DispatcherTimer dt = sender as DispatcherTimer;
                if (dt != null)
                {
                    dt.Stop();
                    dt = null;
                }



                FrameworkElement root = Application.Current.RootVisual as FrameworkElement;
                GetPanelsSetPattern(root);

            }
        }



        void GetPanelsSetPattern(FrameworkElement root)
        {

            FieldInfo[] fields = root.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
            for (int i = 0; i < fields.Length; i++)
            {
                if (fields[i].FieldType.IsSubclassOf(typeof(Panel)))
                {
                    Panel p = fields[i].GetValue(root) as Panel;
                    SetPattern(p);

                }
            }
        }

        void SetPattern(Panel p)
        {

            if (p.Background == this)
            {
                p.Background = null;

                SetPatternImpl(p.ActualWidth, p.ActualHeight);

                p.Children.Insert(0, Pattern);
            }
            else if (this.Pattern != null && p.Children.Contains(this.Pattern))
            {
                SetPatternImpl(p.ActualWidth, p.ActualHeight);
            }

        }

        void SetPatternImpl(double width, double height)
        {
            Pattern = new WrapPanel();
            Pattern.Width = width;
            Pattern.Height = height;
            Pattern.HorizontalAlignment = HorizontalAlignment.Stretch;
            Pattern.VerticalAlignment = VerticalAlignment.Stretch;


            double xObj = (1 / this.Viewport.Width);
            double yObj = (1 / this.Viewport.Height);



            for (int i = 0; i < Math.Ceiling(xObj * yObj); i++)
            {
                Shape ns = this.Drawing.Clone();
                ns.Stretch = this.TileMode == TileMode.None ? Stretch.None : Stretch.Fill;
                ns.Width = Pattern.Width / xObj;
                ns.Height = Pattern.Height / yObj;
                ScaleTransform st = new ScaleTransform();
                st.ScaleX = this.TileMode == TileMode.FlipX | this.TileMode == TileMode.FlipXY ? -1 : 1;
                st.ScaleY = this.TileMode == TileMode.FlipY | this.TileMode == TileMode.FlipXY ? -1 : 1;
                ns.RenderTransform = st;
                Pattern.Children.Add(ns);
            }
        }
    }

    /// <summary>
    /// For some reason there is no TileMode enum anymore in SL2.0 B2
    /// </summary>
    public enum TileMode
    {
        None,
        FlipX,
        FlipY,
        FlipXY
    }
    
}
